package com.example.server.security;

import com.example.common.security.GrantedAuthority;
import com.example.server.security.authorize.AuthorizeDecider;
import com.example.server.security.authorize.HasAnyRoleAuthorizeDecider;
import com.example.server.security.authorize.PreAuthorizeResourceMetaDataProvider;
import com.example.server.security.authorize.ResourceMetaDataProvider;
import com.example.server.security.context.SecurityContextHolder;
import com.example.common.security.UserDetails;
import com.example.common.vo.RespBean;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

@Component
@Data
public class AuthorizeInterceptor implements HandlerInterceptor {

    private ResourceMetaDataProvider resourceMetaDataProvider;

    @Autowired
    private SecurityContextHolder securityContextHolder;

    private AuthorizeDecider authorizeDecider;

    @Autowired
    private PreAuthorizeResourceMetaDataProvider preAuthorizeResourceMetaDataProvider;

    @Autowired
    private HasAnyRoleAuthorizeDecider hasAnyRoleAuthorizeDecider;

    @Value("${yeb.security.enable-authorized-annotation}")
    private boolean enableAuthorize;


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            if (enableAuthorize) {

                this.resourceMetaDataProvider = preAuthorizeResourceMetaDataProvider;

                HandlerMethod handlerMethod = (HandlerMethod) handler;
                PreAuthorize methodAnnotation = handlerMethod.getMethodAnnotation(PreAuthorize.class);

                if (methodAnnotation != null) {
                    String type = methodAnnotation.type();
                    if (type.equals("hasAnyRole")) {

                        this.authorizeDecider = hasAnyRoleAuthorizeDecider;
                    } else if (type.equals("hasAllRoles")) {

                        // TODO
                    }
                }
            }

            /**
             * 1 确定本次请求允许的权限范围
             * 2 收集请求的用户具备的权限范围
             * 3 比较这两者，判断是否有交集
             */

            List<? extends GrantedAuthority> allowedAuthorities
                    = resourceMetaDataProvider.provide(request, response, (HandlerMethod) handler);

            if (allowedAuthorities == null || allowedAuthorities.size() == 0) {
                return true;
            }

            UserDetails userDetails = securityContextHolder.getUserDetails();

            List<? extends GrantedAuthority> hadAuthorities = userDetails.getPerms();

            if (hadAuthorities == null || hadAuthorities.size() == 0) {
                throw RespBean.AUTH_FAILED_ERROR_411.toException();
            }


            boolean decide = authorizeDecider.decide(allowedAuthorities, hadAuthorities);

            if (decide) {
                return true;
            } else {
                throw RespBean.AUTH_FAILED_ERROR_411.toException();
            }
        }
        return true;
    }
}
